Single-responsibility
principle





































يشير"التجميع مع المرجع" ، إلى علاقة التجميع حيث تحتوي إحدى الفئات على مرجع إلى فئة أخرى كجزء من حالتها. إنه يمثل علاقة "جزء كامل" ، حيث يكون للفئة "الكاملة" إشارة إلى فئة "الجزء" ، مما يشير إلى الملكية أو الاحتواء.
قد تحدد الأصناف الأساسية وتنفذ طرقًا افتراضية ، ويمكن للفئات المشتقة أن تتجاوزها
يمكن معاملة كائنات فئة مشتقة ككائنات من فئة أساسية
الميراث الفردي: الفئة المشتقة مشتقة من فئة أساسية واحدة.

الوراثة المتعددة: الفئة المشتقة مشتقة من فئات أساسية متعددة.

الوراثة متعددة المستويات: فئة مشتقة من فئة مشتقة أخرى.

الوراثة الهرمية: يتم إنشاء فئات مشتقة متعددة من فئة أساسية واحدة.

الوراثة الهجينة: مزيج من نوعين أو أكثر من أنواع الميراث.
Framework Design
غالبًا ما تستخدم أطر العمل الأساليب الافتراضية لتوفير نقاط قابلية للتوسعة للمطورين. على سبيل المثال، يسمح ASP.NET للمطورين بتجاوز وتوسيع الأساليب الافتراضية المختلفة لتخصيص سلوك تطبيقات الويب.
Extensibility
public class Logger { public virtual void Log(string message) { Console.WriteLine($"Logging: {message}"); } } public class FileLogger : Logger { public override void Log(string message) { // Custom implementation for file logging // ... } }
Method Overriding
public class Shape
{
public virtual double CalculateArea()
{
// Default implementation
return 0.0;
}
}

public class Circle : Shape
{
public double Radius { get; set; }

public override double CalculateArea()
{
return Math.PI * Radius * Radius;
}
}

Defining Contracts
public interface IDisposable
{
void Dispose();
}
using With Override
يمكنك إعلان طريقة في الفصل على أنها افتراضية. الطريقة الافتراضية هي طريقة في فئة أساسية يمكن تجاوزها بواسطة الفئات المشتقة. وهذا يتيح تجاوز الطريقة، وهي سمة أساسية لتعدد الأشكال. عندما يتم وضع علامة على طريقة افتراضية على أنها افتراضية، يمكن للفئات المشتقة توفير التنفيذ الخاص بها لهذه الطريقة باستخدام الكلمة الأساسية للتجاوز.
Method
virtual
Interface
Encapsulation
Polymorphism
. Abstruct
. Static
public static class Calculator
Calculator cal=new Calculator();
لا يمكن انشاء كائن في
تؤدي static مهمة رائعة جداً فيما يتعلق بالذاكرة، فهي تساعد المبرمج على إنشاء نظام فعال في إستخدام الذاكرة، أي يقلل من إستخدام الذاكرة. و ذلك لأن كل متغير أو دالة أو غيره تم تعريفه بأنه static فذلك يعني أن هذا المتغير -فلنقل- سيتم حجز مكانه في الذاكرة لمرة واحدة فقط.
إستخدم static لتعريف المتغيرات غير القابلة للتغيير منطقياً
كثيراً ما تجدا متغيرات محددة القيمة داخل الفئة ومعرفة بـ final و لكنها غير معرفة بـ static وفي نفس الوقت قيمتها لن تتغير اطلاقاً طوال حياة البرنامج. فلماذا لا تجعلهُ مشتركاً و توفر المساحة بالذاكرة و تُظهر احترافيتك.‌

إستخدم static مع الدوال التي من المنطقي إستخدامها قبل إنشاء object
إذا أردت أن تنشئ class لسيارة، فمن الدوال التي يجب أن تضعها static مثلاً الدالة التي تغير وحدات قياس السرعة بين كيلومتر و ميل. فهذه الدالة قد تستخدمها بدون إنشاء الـ class لإجراء أي تغيير، و حتى بدون أن تمتلك سيارة بمعنى آخر، أما الدوال مثل سرعة السيارة الحالية فيجب أن لا تعرف كـ static لأنها متعلقة بكل سيارة لوحدها.‌

لإجراء تغييرات على متغير static، أنشئ دالة static
ليس دائماً، و لكن الشرط هو أن تكون لديك عمليات معقدة تجريها على متغير static، ففي هذه الحالة أنشئ دالة من النوع static حتى تستطيع أن تستدعيها دون أن تنشئ object، فالمتغير الـ static بالإمكان تغييره و إسناد قيمة له و تعديلها دون أن ينشأ أي object.‌

إستخدم static مع المتغيرات المُشتركة بين الـ objects
إبحث دائماً عنن المتغيرات المشتركة بين الـ objects، هذه المتغيرات إذا ما تغير في أحد الـ classes سوف تتغير في البقية،عند تعريفك لـ class يخص طالب في جامعة، فمن المتغيرات التي يجب أن تعرف بـ static إسم الجامعة و تاريخ إنشاءها، لماذا؟ هذه المتغيرات غير قابلة للتغيير على مستوى الطلاب، فإذا أنشأت عدة objects فلن يتغير إسم الجامعة، أليس كذلك؟ و هنا تأتي فائدة static بأنه مهما كان عدد الـ objects تبقى المساحة المحجوزة للمتغير الخاص بإسم الجامعة واحدة، و لكن المتغيرات مثل إسم الطالب و عمره يتم إنشاءها مع كل object لوحده.‌

إستخدم static مع الدوال غير المتعلقة بالـ object
توجد بعض الدوال التي لا تتعلق بالـ object الذي سيتم إنشاؤه، و لكن يتم إنشاؤها كدوال مساعدة، ليس بالضرورة أن تكون هذه الدوال من النوع الذي يُستدعى قبل إنشاء الـ object كما ذكرتُ لك سابقاً، بل أي دالة مساعدة غير متعلقة بالـ object إجعلها static لتوفر بعض المساحة بالذاكرة.
1.
2.
3.
4.
5.
. Overloding
هو ميزة تتيح للفصل أن يكون له أكثر من طريقة واحدة بنفس الاسم ، بشرط أن تكون معلماتها مختلفة. يكون هذا مفيدًا عندما تحتاج إلى نفس وظائف الطريقة لأنواع مختلفة من البيانات.
. Encapsulation التغليف
. Override
public virtual void Method() { // implementation }
{
1
{
2
public override void Method() { // implementation }
{
3
public override void Method() { base.Method(); // implementation }
. Association
. Aggregation
. Composition
يمثل التكوين علاقة قوية "بالجزء الكامل" بين الطبقات ، حيث تكون فئة المكون جزءًا لا يتجزأ من الطبقة المركبة
İlişki or Bağlantı
Birleştirme
Bileşim
Bileşim
Abstract classes are used in object-oriented programming when we want to provide a common interface, define common behavior, or establish a base structure for a group of related classes. Here are some scenarios where abstract classes are commonly used:

Creating a Base Class: Abstract classes serve as a blueprint for derived classes. They provide common members, such as properties, methods, or events, that are shared among multiple derived classes. The abstract class acts as a base class from which other classes inherit.

Defining a Common Interface: Abstract classes can define a common set of methods or properties that derived classes must implement. This ensures that all derived classes adhere to the same interface, enabling polymorphism and allowing objects of different derived classes to be treated interchangeably.

Partial Implementation: Abstract classes can contain both abstract and non-abstract members. Abstract members are declared without an implementation, while non-abstract members have a complete implementation. This allows the abstract class to provide a default implementation for some members while leaving others to be implemented by derived classes.

Providing Default Functionality: Abstract classes can define default behavior that derived classes can inherit. This reduces code duplication by providing a common implementation for certain methods or properties. Derived classes can then choose to override or extend the default behavior as needed.

Frameworks and APIs: Abstract classes are commonly used in frameworks or APIs to provide a foundational structure and define contracts that derived classes must adhere to. They allow developers to extend the framework or API by creating their own derived classes while leveraging the predefined behavior and structure of the abstract class.

Overall, abstract classes are used to establish a common structure, behavior, or interface that derived classes can inherit and build upon. They provide a level of abstraction and help in organizing and structuring code in an object-oriented manner.
Static members, such as static fields, properties, methods, or classes, are used in object-oriented programming when we want to define elements that are shared among all instances of a class or that belong to the class itself rather than individual objects. Here are some scenarios where static members are commonly used:

Shared Data: Static fields are used to store data that needs to be shared among all instances of a class. For example, a static counter can be used to keep track of the total number of instances created.

Utility Methods: Static methods are commonly used for utility functions or helper methods that don't require access to instance-specific data. These methods can be invoked directly on the class itself without the need to create an instance.

Constants: Static fields can be used to define constants that hold values that remain the same across all instances of a class. These constants can be accessed without creating an instance of the class.

Factory Methods: Static methods can be used as factory methods to create instances of a class. These methods encapsulate the logic of object creation and provide a convenient way to create objects without directly invoking the constructor.

Math Functions: Many math functions in programming languages are defined as static methods in math-related classes. These functions are commonly used and don't require any state information.

Singleton Pattern: The Singleton design pattern is often implemented using a static property or method to ensure that only a single instance of a class is created and accessed throughout the application.

Static members are associated with the class itself rather than individual instances. They are accessed using the class name and provide a way to define behavior or data that is common to all instances or doesn't require an instance to be accessed.
Encapsulation is a fundamental principle in object-oriented programming that involves bundling data and methods together into a single unit called a class. It provides a way to hide the internal implementation details of a class and expose a controlled and well-defined interface to interact with the class. Here are some scenarios where encapsulation is commonly used:

Data Protection: Encapsulation helps in protecting the data within a class by hiding it from direct access outside the class. Data members can be made private or protected, preventing direct modification by external code. Instead, controlled access to the data is provided through methods (getters and setters) to enforce data integrity and maintain consistent state.

Information Hiding: Encapsulation allows the internal implementation details of a class to be hidden from the outside world. The class can provide a well-defined interface consisting of public methods, properties, and events to interact with the class, while keeping the internal workings hidden. This protects the class from unwanted modifications and allows for easier maintenance and refactoring.

Code Modularity and Reusability: Encapsulation promotes code modularity by encapsulating related data and methods into a single unit. This makes the code more organized, readable, and maintainable. Additionally, encapsulated classes can be easily reused in other parts of the program or in other projects, as they provide a clear interface without exposing the internal implementation details.

Enhanced Flexibility and Extensibility: By encapsulating the implementation details, changes to the internal workings of a class can be made without affecting the external code that uses the class. This improves flexibility, as modifications within the class do not require changes in external code. It also allows for easy extension of functionality by adding new methods or properties while keeping the existing interface intact.

Access Control: Encapsulation allows control over the visibility and accessibility of class members. Private members are only accessible within the class, while public members can be accessed by external code. This control ensures that the class exposes only what is necessary, reducing the risk of unintended modifications and ensuring proper encapsulation.

Overall, encapsulation helps in organizing and structuring code, protecting data integrity, promoting reusability, and providing a clear and controlled interface to interact with a class. It is a key principle in object-oriented programming that contributes to writing maintainable, modular, and robust code.
Overloading is used in programming when we want to define multiple methods or functions with the same name but different parameters within a class or in a set of related functions. Here are some scenarios where overloading is commonly used: Method Variations: Overloading allows us to create multiple versions of a method that perform similar operations but with different parameter types or different numbers of parameters. This enables flexibility in method usage and provides convenience to the developers by allowing them to call the same method name with different arguments based on their specific needs. Default Parameters: Overloading can be used to define methods with default parameter values. By providing overloaded versions of a method with fewer parameters, we can set default values for the missing arguments. This simplifies method invocation, as some parameters can be omitted if their default values are sufficient. Type Conversion: Overloading can be used to define methods that accept different types of parameters, allowing for automatic type conversion. For example, a method may have an overload that accepts an integer as a parameter and another overload that accepts a string. The appropriate overload is called based on the type of argument provided. Method Signatures: Overloading enables the creation of methods with the same name but different signatures, which includes the method's name and parameter types. This helps in enhancing code readability and maintainability by allowing developers to choose meaningful and descriptive method names while still having a consistent naming scheme. Polymorphism: Overloading is an essential aspect of polymorphism, which allows objects of different types to be treated uniformly. By defining multiple overloaded methods with the same name but different parameters, we can achieve method overloading and polymorphic behavior. Overall, overloading provides a way to define multiple versions of methods or functions that share the same name but have different parameters. It improves code readability, enhances flexibility, and allows for easier method invocation by providing variations based on parameter types, default values, or different numbers of parameters.
The "override" keyword is used in object-oriented programming to indicate that a method in a derived class is intended to override a method with the same name and signature in its base class. Here are some scenarios where the "override" keyword is commonly used: Inheritance: When a derived class inherits from a base class, it can override the implementation of certain methods inherited from the base class. This allows the derived class to provide its own implementation of the method, tailored to its specific needs. Polymorphism: Overriding methods plays a crucial role in achieving polymorphic behavior. Polymorphism allows objects of different types to be treated uniformly based on their common base class or interface. By overriding a method in a derived class, we can provide specialized behavior while still adhering to the contract defined by the base class or interface. Customization and Extension: Overriding methods enables customization and extension of behavior. Derived classes can inherit the base implementation from the base class and modify or extend it as required. This allows for fine-tuning or adding additional functionality to meet specific requirements. Runtime Polymorphism: When a method is overridden, the appropriate implementation is dynamically determined at runtime based on the actual type of the object. This allows for dynamic dispatch, where the correct version of the method is invoked based on the runtime type of the object, contributing to the flexibility and extensibility of the program. Virtual Methods: The base class must mark a method as "virtual" to allow derived classes to override it. The "virtual" keyword in the base class indicates that the method can be overridden in derived classes, providing a mechanism for method overriding. By using the "override" keyword, we explicitly indicate that a method in a derived class is intended to replace the implementation of the same-named method in the base class. This enables customization, specialization, and polymorphic behavior in object-oriented programming.
Inheritance is used in object-oriented programming when we want to create a new class based on an existing class, known as the base class or parent class. The new class, called the derived class or child class, inherits the properties, methods, and behavior of the base class. Here are some scenarios where inheritance is commonly used:

Code Reusability: Inheritance allows us to reuse the code from an existing class. The derived class inherits all the members (fields, properties, methods, etc.) of the base class, eliminating the need to rewrite the same code. This promotes code reuse and reduces redundancy.

Class Hierarchy: Inheritance enables the creation of class hierarchies. We can organize related classes into a hierarchy, with more general or abstract classes at higher levels and more specialized or specific classes at lower levels. This hierarchical structure provides a way to model real-world relationships and concepts.

Polymorphism: Inheritance plays a vital role in achieving polymorphism. Polymorphism allows objects of different types to be treated uniformly based on their common base class. By using inheritance, we can write code that operates on the base class and can work with instances of both the base class and its derived classes.

Method Overriding: Inheritance allows derived classes to override methods inherited from the base class. This enables customization and specialization of behavior in the derived class. By providing a different implementation for a method, the derived class can tailor the behavior to its specific needs while still adhering to the interface defined by the base class.

Extensibility: Inheritance facilitates the extension of existing classes. We can create new classes that inherit from the base class and add new members or modify existing ones. This allows for easy addition of new functionality without modifying the original class, promoting the open-closed principle of software design.

Abstraction and Generalization: Inheritance allows for the creation of abstract base classes that define a common interface or behavior shared by multiple derived classes. It helps in capturing common attributes and behavior at a higher level and provides a foundation for specialized classes to build upon.

In summary, inheritance is used to reuse code, create class hierarchies, achieve polymorphism, customize behavior through method overriding, extend existing classes, and provide abstraction and generalization. It promotes code reuse, modularity, and flexibility in object-oriented programming.
Enums, short for enumerations, are used in programming to define a set of named constant values. They provide a way to represent a fixed set of possible values for a specific variable or property. Here are some scenarios where enums are commonly used: Representing Categories or Options: Enums are often used to represent different categories, types, or options within a program. For example, days of the week, months of the year, status codes, or menu options can be represented using enums. Enhancing Readability and Maintainability: Enums improve code readability by providing meaningful names for specific values. Instead of using raw numeric or string literals, enums allow developers to use descriptive names, making the code more understandable and maintainable. Providing Type Safety: Enums provide type safety by ensuring that the variable or property can only take one of the predefined constant values. This helps catch potential programming errors at compile-time and improves code robustness. Switch Statements: Enums are commonly used in switch statements to handle different cases or options. By using enums as switch cases, it provides a concise and readable way to handle specific behavior based on the selected enum value. Configuration and Settings: Enums can be used to represent different configuration options or settings within a program. This allows for easy and centralized management of configurable values, ensuring consistency and maintainability. API Design: Enums are often used in API design to define sets of valid values or options for method parameters or return types. This helps in providing clear and self-documenting APIs that enforce valid input values. Protocol and State Definitions: Enums can be used to define different states or protocol definitions in a program. For example, in a game, the different states such as "playing," "paused," or "game over" can be represented using enums. In summary, enums are used to define a set of named constant values. They improve code readability, provide type safety, enhance maintainability, and are commonly used in scenarios involving categories, options, switch statements, configuration, API design, and state definitions. Enums offer a convenient and expressive way to work with predefined constant values in a program.
. Polymorphism
تعدد الأشكال
Overriding and Overloding is a form of polymorphism
Polymorphism is used in object-oriented programming when we want to treat objects of different classes in a unified manner based on their common interface or base class. It allows us to write code that can work with objects of different types, enabling flexibility and extensibility. Here are some scenarios where polymorphism is commonly used:

Code Flexibility: Polymorphism allows us to write flexible code that can work with different types of objects. By using a common interface or base class, we can write code that is not dependent on specific implementations but instead focuses on the shared behaviors and capabilities defined by the common interface.

Method Overriding: Polymorphism often involves method overriding, where a derived class provides its own implementation of a method inherited from a base class or interface. Through method overriding, different objects of derived classes can be treated uniformly, and the appropriate version of the method is invoked based on the actual type of the object.

Interface-Based Programming: Polymorphism is essential when working with interfaces. By programming to an interface rather than a specific implementation, we can achieve loose coupling and easily swap out different implementations as needed. This promotes modularity, maintainability, and allows for future extensibility.

Collections and Generics: Polymorphism is extensively used when working with collections or using generic programming. By using a common base class or interface, we can create collections that can store objects of different derived types or use generic types that can work with a wide range of types.

Inheritance Hierarchies: Polymorphism plays a crucial role in inheritance hierarchies. Derived classes can inherit from a base class and provide specialized behavior while still adhering to the common interface or behavior defined by the base class. This allows objects of different derived classes to be treated uniformly as instances of the base class.

Runtime Behavior: Polymorphism allows the behavior of a program to be determined at runtime based on the actual type of the objects involved. This dynamic dispatch ensures that the appropriate version of a method is invoked based on the runtime type of the object, providing flexibility and extensibility in code execution.

In summary, polymorphism allows objects of different types to be treated uniformly based on their common interface or base class. It enables code flexibility, method overriding, interface-based programming, working with collections and generics, handling inheritance hierarchies, and achieving dynamic behavior at runtime.



















































Interface
Virtual
Realization
Association
Aggregation
Composition
enum
static
Abstract
Overloading
Overriding
Inheritance


public class Car : IVehicle, IDriver { // Implement IVehicle and IDriver methods here }
Multiple Inheritance

Polymorphism
إنه عقد يحدد مجموعة من الأساليب والخصائص والأحداث والمفهرسات المجردة (غير المنفذة). يجب على أي فئة تنفذ واجهة أن توفر تعريفات ملموسة (منفذة) لجميع الأعضاء المحددين في تلك الواجهة. تُستخدم الواجهات لتحديد مجموعة مشتركة من الوظائف التي يمكن لفئات متعددة مشاركتها.
OUTPUT : 5
استخدم المُعدِّل المجرد في إعلان الفئة للإشارة إلى أن الفئة تهدف فقط إلى أن تكون فئة أساسية من الفئات الأخرى ، وليس إنشاء مثيل لها بمفردها.




Realization, also known as implementation, is used in object-oriented programming when we want to provide concrete definitions for abstract classes or interfaces. Here are some scenarios where realization is commonly used:

Abstract Classes: When you have an abstract class that defines a set of methods or properties without providing their implementation details, you need to create a concrete class that derives from the abstract class and provides the actual implementation for those abstract members.

Interfaces: When you have an interface that declares a contract or a set of method signatures, you need to create a class that implements that interface and provides the implementation for those interface members.

Polymorphism: Realization enables polymorphism, where objects of different classes that realize the same abstract class or interface can be used interchangeably. This allows for writing flexible and reusable code that can work with different implementations of a common interface.

Code Modularity and Separation of Concerns: Realization helps in separating the interface or abstract class from its concrete implementation. This promotes code modularity and separation of concerns, making it easier to maintain, extend, and test the codebase.

Frameworks and APIs: Realization is frequently used when developing frameworks or APIs. Abstract classes and interfaces define the contract that client code can rely on, while realization provides the actual implementation of those contracts.

By using realization, you can define abstract classes or interfaces that establish a common contract or behavior, and then create concrete classes that realize or implement those abstract classes or interfaces to provide the specific functionality required for each class.

.Generalization / Inheritance


Association is used in object-oriented programming to represent relationships between classes or objects. It defines how two or more classes are connected or interact with each other. Here are some scenarios where association is commonly used: Relationship Modeling: Association is used to model real-world relationships between objects. For example, in a university system, there could be an association between the "Student" class and the "Course" class to represent the enrollment of a student in a course. Collaboration and Interaction: Association represents how classes collaborate or interact with each other to accomplish certain tasks. It describes the communication and dependencies between classes. Code Reusability: Association allows for code reuse by connecting different classes. By associating classes, you can leverage the functionality and behavior of one class in another class without inheritance. Multiplicity and Cardinality: Association can specify the multiplicity and cardinality of the relationship between classes. It defines how many objects can be associated with each other and the constraints on the association. Dependency Management: Association helps manage dependencies between classes. It allows one class to depend on another class, and changes in one class may affect the other class. System Design and Architecture: Association plays a crucial role in system design and architecture. It helps in designing the structure and interactions between classes and can guide the overall design of a software system. Frameworks and APIs: Association is often used in the design and implementation of frameworks and APIs. It allows different components and modules to work together by establishing associations between them. In summary, association is used to represent relationships, interactions, and dependencies between classes or objects. It is a fundamental concept in object-oriented programming that helps in modeling real-world scenarios, promoting code reuse, managing dependencies, and guiding system design.



Aggregation is used in object-oriented programming to represent a "has-a" relationship between classes or objects, where one class has a reference to another class as a part of its state. Aggregation implies a looser relationship compared to composition, where the associated class retains its independent existence and can be shared among multiple instances of the main class. Here are some scenarios where aggregation is commonly used: Whole-Part Relationship: Aggregation is used to model a whole-part relationship between classes. For example, a "Car" class may have an aggregation relationship with a "Wheel" class, where a car consists of multiple wheels. Code Reusability: Aggregation allows for code reuse by referencing existing classes as parts of other classes. Instead of duplicating the code or behavior, a class can refer to another class and leverage its functionality. Flexibility and Extensibility: Aggregation provides flexibility in managing the relationship between classes. It allows for dynamic association and allows the associated class to be swapped or modified without affecting the main class. Multiplicity and Cardinality: Aggregation can define multiplicity and cardinality, specifying how many instances of the associated class can be part of the main class. For example, a car may have multiple wheels, and the multiplicity can be specified as "1 to many". Database Modeling: Aggregation is commonly used in database modeling, where it represents the relationship between tables or entities. Aggregation can be used to define relationships such as one-to-many or many-to-many between database entities. Frameworks and APIs: Aggregation is often used in the design and implementation of frameworks and APIs. It allows different components or modules to be aggregated together to build a larger system or structure. In summary, aggregation is used to represent a whole-part relationship between classes, where one class has a reference to another class. It promotes code reuse, flexibility, and extensibility while allowing the associated class to retain its independent existence. Aggregation provides a way to structure classes and model relationships in object-oriented programming.






Composition is used in object-oriented programming to represent a strong "has-a" relationship between classes or objects, where one class is composed of other classes as its parts or components. Composition implies ownership and lifecycle management, where the component objects cannot exist independently of the composite object. Here are some scenarios where composition is commonly used: Whole-Part Relationship: Composition is used to model a strong whole-part relationship between classes. For example, a "Car" class may have a composition relationship with an "Engine" class, where the engine is an integral part of the car. Encapsulation and Abstraction: Composition helps in encapsulating related classes into a single entity. It allows for the creation of higher-level abstractions by combining multiple components into a composite class. Lifecycle Management: Composition involves the ownership and lifecycle management of the component objects. The lifespan of the component objects is tightly coupled to the lifespan of the composite object. When the composite object is created or destroyed, its component objects are created or destroyed accordingly. Code Organization and Modularity: Composition promotes code organization and modularity by breaking down complex systems into smaller, manageable components. Each component can encapsulate specific functionality, leading to a more modular and maintainable codebase. Behavior Extension: Composition allows for behavior extension by combining multiple classes and their functionalities. By composing objects, a class can inherit and leverage the behaviors and capabilities of its components, adding more functionality to the composite class. Component Reusability: Composition facilitates reusability by using existing classes as components. Instead of duplicating code or functionality, a composite class can refer to and reuse existing classes as its components, promoting code reuse and reducing redundancy. Frameworks and APIs: Composition is commonly used in the design and implementation of frameworks and APIs. It allows developers to assemble and configure different components to build complex systems or structures. In summary, composition is used to represent a strong whole-part relationship between classes, where one class is composed of other classes as its components. It involves ownership, lifecycle management, and encapsulation. Composition promotes code modularity, behavior extension, reusability, and is commonly used in frameworks and APIs.
تمتلك الفئة المركبة وتدير دورة حياة فئة المكون. لا يمكن أن يوجد المكون بشكل مستقل خارج المركب.
OOP/UML
SOLID





Open-closed principle
Liskov substitution principle
Interface segregation principle
Dependency Inversion Principle


Sınıflarımız/fonksiyonlarımız değişikliğe kapalı ancak yeni davranışların eklenmesine açık olmalıdır.
Bu prensip; sürdürülebilir ve tekrar kullanılabilir yapıda kod yazmanın temelini oluşturur.

Robert C. Martin

Open Sınıf için yeni davranışlar eklenebilmesini sağlar. Gereksinimler değiştğinde, yeni gereksinimlerin karşılanabilmesi için bir sınıfa yeni veya farklı davranışlar eklenebilir olmasıdır.

Closed Bir sınıf temel özelliklerinin değişimi ise mümkün olmamalıdır.
E
xample:
Area servisimiz dikdörtgen hesabını yapacak seviyede, ancak bizim ihtiyaçlarımız daire hesabının da yapılmasını da gerektirmeye başladı diyelim.

AreaService tüm şekil tiplerinin alan hesabını yapmakla yükümlü ancak her alanın da kendine özgü bir hesaplama yöntemi mevcut, bu cümleden de anlaşılacağı üzere her şekil için farklı hesaplama yöntemi, her şekil için kendi içlerinde hesaplama gerekliliğini doğurmaktadır.

Bunu çözmek için bir Shape interface’imiz olsa ve her bir şekil için hesaplanmış area’yı dönse nasıl olur?
Her şekil Shape üzerinden türetilmelidir. Burada açıkça görülmektedir ki; Şekillerden biri olan Dikdörtgen alan hesabını getArea metodumla öğrenebiliriz.

Example:
ingle Responsibility; Tek işi, tek sorumlulukta yapma sanatı…
Her sınıf, metot, fonksiyon tek bir sorumluluğa sahip olmalıdır.
Şayet bu kurala uymazsak ilerleyen süreçte bir değişikliğe gidildiğinde bunun etkisini birçok yerde görmüş oluruz.
Nedeni ise bir yapıya birden fazla sorumluluk yüklenmesinden dolayıdır.
Eğer değişikliklerden etkilenen yerler arasında sistemin birçok yerinde kullanılan bir yapımız da varsa maliyet gittikçe artacaktır.

Yukarıdaki diyagrama ve koda baktığımızda Person sınıfı içerisinde
sendPasswordResetLink() diye bir metot bulunmaktadır. Bu sınıfın asıl amacı kişilere ait bilgileri tutmaktır, şifre sıfırlama bağlantısı göndermek değil.
Birden fazla sorumluluk yüklendiği için olası bir mail gönderme değişikliğinde bu sınıf da etkilenecektir.

Yukarıdaki UML diyagramını biraz daha düzenlersek aşağıdaki gibi bir yapı elde edilir.
S
.Artık programımız Open/Closed prensibine uygun hale gelmiştir.

.Herhangi bir yeni şekil alanı hesaplamamız gerektiğinde yapmamız gereken AreaService üzerinde değişiklik değil, ki değişikliğe kapalı olmalıyız.

.Shape nesnemizden yeni şekli türetmemiz ve alan hesabını kendi içinde yapmamızdır. Böylece genişlemeye açık oluyoruz ve hiç bi yerde değişikllik yapmamıza gerek kalmıyor.



O
L
.Kodumuzda herhangi bir değişiklik yapmaya gerek kalmadan türetilmiş sınıfları (sub class) türedikleri ata sınıfın (base class) yerine kullanabilmeliyiz.

. “Alt seviye sınıflardan oluşan nesnelerin/sınıfların, ana(üst) sınıfın nesneleri ile yer değiştirdikleri zaman, aynı davranışı sergilemesi gerekmektedir. Türetilen sınıflar, türeyen sınıfların tüm özelliklerini kullanabilmelidir.”
.Alt sınıflar, üst sınıflardan türediği için onların davranışlarını devralırlar. Eğer üst sınflara ait davranışları gerçekleştirmiyorlarsa davranışı yapan metotu muhtemelen boş bırakır ya da bir hata fırlatırız fakat bu işlemler kod kirliliğine ve gereksiz kod kalabalığına neden olmaktadır.

Bunların yanı sıra projeye daha sonradan dahil olacak geliştiriciler için de sorun oluşturmaktadır. Geliştirici, sistemin sağlıklı yürüdüğünü düşünerek gerçekleştirilmeyen bir davranışı kullanmaya çalışabilir.


Koda baktığımız zaman DatabaseLogger sınıfımız, Logger adlı sınıftan türemektedir. Başlangıç aşaması için bir problem görünmezken ilerleyen zamanlarda veri tabanı değil de bir dosyaya kayıt işlemi alınacağı zaman aşağıdaki gibi bir görünüm meydana gelecektir.

Bağlantı açma ve kapatma işlemleri veri tabanına aittir, bir dosyaya değil. Gereksiz hata fırlatmaları, kodun okunmasındaki zorluk, kod kalabalığı gibi birçok olaya neden olmaktadır. Burada bu işlemler bir ara sınıfa alınabilir.

İ


D
Bu tür durumlarda bu prensip bizlere bu arayüzlerin ayrılmasını ve ihtiyaç halinde olanların kullanılmasını söylemektedir.
Önceki UML diyagramını biraz daha düzenlersek aşağıdaki gibi bir yapı elde edilir.

work() , pay() , eat() davranışları başka arayüzlere aktarıldı ve ihtiyaç halinde olanlar uygulandı.

Yüksek seviye sınıflar, düşük seviye sınıflara bağlı olmamalıdır. Her ikisi de soyutlamalara bağlı olmalıdır.
Soyutlamalar, detaylara bağlı olmamalıdır. Detaylar, soyutlamalara bağlı olmalıdır.
Yukarıdaki diyagram ve kod incelendiğinde ExceptionReporter sınıfının (yüksek seviyeli sınıf)

, OracleDatabase sınıfına (düşük seviyeli sınıf) direkt olarak bağımlı olduğu görülmektedir.

İleride veri tabanı olarak Oracle değil de MySQL kullanmak istersek maalesef bu sınıfa müdahale etmek zorunda kalacağız.

Bu istenmeyen bir davranıştır. Bunun çözümünü ise buradaki bağımlılıkları soyutlayarak sağlayacağız.

İlk baştaki UML diyagramını biraz daha düzenlersek yandaki gibi bir yapı elde edilir.
Geliştirdiğimiz yazılıma/sınıfa var olan kodu değiştirmeden, yeni kod yazılarak yeni özellikler eklenebilmelidir.

Yeni bir gereksinim geldiğinde mevcut kod üzerinde herhangi bir değişiklik yapıyorsanız, open/closed prensibine ters düşüp düşmediğinizi kontrol etmenizde yarar var.

Yazılımı geliştirirken gelecekte oluşabilecek özellikler ve geliştirmeleri her şeyiyle öngöremeyiz. O yüzden oluşabileceğini düşündüğümüz kodları da şimdiden geliştirmemeliyiz.

Yeni gelecek özellikler için var olan kodu değiştirmeden, var olan yapıyı bozmadan esnek bir geliştirme modeli uygulayarak, önü açık ve gelecekten gereksinimlere kolayca adapte olup, ayak uydurabilen bir model uygulamalıyız.
Daire alan hesabı yapabilmek için AreaService’i içindeki calculateArea methodumuzda değişiklik yapmamız gerekti.

Yeni bir şekil eklemek istediğimizde üçgen gibi sürekli bu metot üzerinde değişiklikler yapacağız ve durum giderek kötüleşecek ve oluşan durum open/closed prensibine uymadığımızı gösteriyor.

Bu durum için sınıfımız/metodumuz değişikliğe kapalı değil aksine değişiklik zorunlu hale gelmiştir.

Genişleme ise seçenekler arasında değildir. Her yeni şekil eklenmesi için AreaService üzerinde değişikliğe gitmemiz gerekiyor.

ÖZET: Bir sınıf (nesne) yalnızca bir amaç uğruna değiştirilebilir, o da o sınıfa yüklenen sorumluluktur, yani bir sınıfın(fonksiyona da indirgenebilir) yapması gereken yalnızca bir işi olması gerekir.

ÖZET: Bir sınıf ya da fonksiyon halihazırda var olan özellikleri korumalı ve değişikliğe izin vermemelidir. Yani davranışını değiştirmiyor olmalı ve yeni özellikler kazanabiliyor olmalıdır.

ÖZET: Kodlarımızda herhangi bir değişiklik yapmaya gerek duymadan alt sınıfları, türedikleri(üst) sınıfların yerine kullanabilmeliyiz.


Single-responsibility
principle
ÖZET: Sorumlulukların hepsini tek bir arayüze toplamak yerine daha özelleştirilmiş birden fazla arayüz oluşturmalıyız.
ÖZET: Sınıflar arası bağımlılıklar olabildiğince az olmalıdır özellikle üst seviye sınıflar alt seviye sınıflara bağımlı olmamalıdır.
الإدراك ، المعروف أيضًا باسم التنفيذ ، هو مفهوم في البرمجة الشيئية التي تتضمن تقديم تعريف ملموس لفئة أو واجهة مجردة. إنها عملية إنشاء فئة ترث من فئة مجردة أو تنفذ واجهة وتوفر التطبيقات اللازمة لأعضاء الملخص أو الواجهة. يسمح الإدراك بإنشاء فئات تتوافق مع عقد أو واجهة محددة ، مما يتيح تعدد الأشكال ويضمن إمكانية استخدام الكائنات بالتبادل بناءً على واجهتها المشتركة أو فئة مجردة. السمات الرئيسية للإدراك هي: فئات أو واجهات مجردة: يشمل الإدراك العمل مع فئات مجردة أو واجهات تحدد عقدًا أو مجموعة من إقرارات الأعضاء دون تحديد تفاصيل التنفيذ. توفير تطبيقات: يجب أن توفر الفئة التي تدرك فئة مجردة أو تنفذ واجهة تطبيقات ملموسة لجميع أعضاء الملخص أو الواجهة. هذا يضمن أن الوظيفة المحددة في فئة أو واجهة مجردة يتم تنفيذها في فئة التحقيق. الالتزام بالعقد: يجب أن تلتزم الفئة المحققة بالعقد المحدد بواسطة فئة أو واجهة مجردة. يجب أن توفر تطبيقات تفي بمتطلبات ودلالات الفئة المجردة أو أعضاء الواجهة. تمكين تعدد الأشكال: يسمح الإدراك باستخدام كائنات من فئات مختلفة بالتبادل بناءً على واجهتها المشتركة أو فئة مجردة. يتيح ذلك تعدد الأشكال ، حيث يمكن لمتغير أو معلمة من فئة مجردة أو نوع واجهة أن تشير إلى حالات لفئات تحقيق مختلفة. يتم تحقيق الإدراك عادةً من خلال وراثة الفئة أو تنفيذ الواجهة. يتم استخدام الوراثة لتحقيق فئات مجردة ، بينما يتم استخدام تنفيذ الواجهة لتحقيق الواجهات.
Arayüzler ile sınıflar arasındaki ilişkiyi modellemek için kullanılır. Dashed (kesikli) çizgi ile ifade edilir. Kalıtımdaki çizginin kesik kesik olan halidir.
هو العملية التي يحدد فيها الفئة المشتقة نفس الطريقة التي تم تحديدها في فئتها الأساسية.
main()
يعتبر التغليف درعًا وقائيًا يمنع الوصول إلى البيانات بواسطة الكود خارج هذا الدرع
يتم استخدام التغليف لمنع تغيير الكود (البيانات) عن طريق الخطأ من الوظائف الخارجية
التعداد هو "فئة" خاصة تمثل مجموعة من الثوابت (متغيرات غير قابلة للتغيير / للقراءة فقط).
. enum
تعدد الأشكال هو المكان الذي لا تكون فيه متأكدًا من نوع الكائنات في وقت التشغيل ويتم استدعاء الطريقة الأكثر تحديدًا. لذلك قد يختلف سلوك الأسلوب الذي تم استدعاؤه ، اعتمادًا على نوع الكائنات في وقت التشغيل.
Java'daki polimorfizm, tek bir eylemi farklı şekillerde gerçekleştiren görevdir
Soyut anahtar kelime, sınıflar ve yöntemler için kullanılan erişim dışı bir değiştiricidir: Soyut sınıf: nesneler oluşturmak için kullanılamayan kısıtlı bir sınıftır (erişmek için başka bir sınıftan miras alınması gerekir)
Java'daki statik anahtar kelime, belirli bir sınıfın aynı değişkenini veya yöntemini paylaşmak için kullanılır

lower coupling
Organized
easier to understand
Tasting
features
yüksek
bağımlılık
Artık ikisinin Interface ile bağımlılıklarını kurduk .
high Dep.
low Dep.
(less details)
(over details)
(over details)

low Dep.

Yazılım mimarisi Mimarlık bir sistemin planı olarak hizmet eder. Sistem karmaşıklığını yönetmek ve bileşenler arasında iletişim ve koordinasyon mekanizması kurmak için bir soyutlama sağlar. Performans ve güvenlik gibi ortak kalite özelliklerini optimize ederken, tüm teknik ve operasyonel gereksinimleri karşılayacak yapılandırılmış bir çözümü tanımlar. Ayrıca, yazılım geliştirmeyle ilgili organizasyon hakkında bir dizi önemli kararı içerir ve bu kararların her biri kalite, sürdürülebilirlik, performans ve nihai ürünün genel başarısı üzerinde önemli bir etkiye sahip olabilir. Bu kararlar aşağıdakilerden oluşur: Sistemi oluşturan yapısal elemanların ve bunların arayüzlerinin seçimi. Bu unsurlar arasındaki işbirliğinde belirtilen davranış. Bu yapısal ve davranışsal öğelerin büyük alt sistemler halinde bileşimi. Mimari kararlar iş hedefleriyle uyumludur. Mimari tarzlar organizasyona yön verir.
Mimarlık Hedefleri Mimarinin temel amacı uygulamanın yapısını etkileyen gereksinimleri belirlemektir. İyi yerleştirilmiş bir mimari, teknik bir çözüm oluşturmayla ilişkili iş risklerini azaltır ve iş ile teknik gereksinimler arasında bir köprü kurar. Diğer hedeflerden bazıları şunlardır: Sistemin yapısını açığa çıkarın ancak uygulama ayrıntılarını gizleyin. Tüm kullanım durumlarını ve senaryoları gerçekleştirin. Çeşitli paydaşların gereksinimlerini karşılamaya çalışın. Hem işlevsel hem de kalite gereksinimlerini karşılayın. Sahip olma hedefini azaltın ve kuruluşun pazar konumunu iyileştirin. Sistemin sunduğu kaliteyi ve işlevselliği geliştirin. Kuruluşa veya sisteme dış güveni artırın.
Sınırlamalar Yazılım mimarisi, yazılım mühendisliği içerisinde halen gelişmekte olan bir disiplindir. Aşağıdaki sınırlamalara sahiptir: Mimariyi temsil edecek araçların ve standartlaştırılmış yolların eksikliği. Mimarinin gereksinimleri karşılayan bir uygulamayla sonuçlanıp sonuçlanmayacağını tahmin edecek analiz yöntemlerinin eksikliği. Yazılım geliştirmede mimari tasarımın önemi konusunda farkındalık eksikliği. Yazılım mimarının rolünün anlaşılmaması ve paydaşlar arasındaki zayıf iletişim. Tasarım süreci, tasarım deneyimi ve tasarımın değerlendirilmesi konusundaki anlayış eksikliği.
http://blog.alisuleymantopuz.com/2014/08/30/yazilim-mimarisi-ve-tasarimi-nedir/ * http://www.akifsahman.com/?p=175
https://ece.uwaterloo.ca/~se464/08ST/index.php?src=lecture * http://info.psu.edu.sa/psu/cis/azarrad/se505.htm
http://www.metinakbulut.com/YAZILIM-MIMARISI/ * http://ceng.gazi.edu.tr/~hkaracan/source/YPY_H3.pdf
http://iiscs.wssu.edu/drupal/node/3399 * http://www.cs.toronto.edu/~sme/CSC340F/slides/21-architecture.pdf
http://www.users.abo.fi/lpetre/SA10/ *http://sulc3.com/model.html
http://salyangoz.com.tr/blog/2013/11/23/digerleri/yazilim-gelistirme-surec-modelleri-3/

[ 1 Martin, Micah, and Robert C Martin Agile principles, patterns, and practices in C# Pearson Education, 2006 [ 2 Kniberg Henrik .."Scrum and XP from the Trenches Lulu com 2007 [ 3 Kniberg Henrik What is agile 2013 [ 4 http en wikipedia org/wiki/Agile_software_development [ 5 http www mshowto org/microsoft visual studio team foundation server nedir html [ 6 www kurumsaljava com/download/ 10

UML Diagram Examples


Vize Sorulari






Factory


Strategy
Singlethon





Factory
Strategy
Factory tasarım deseni (Factory Design Pattern), creational (oluşturucu) tasarım desenlerinden biridir. Bu desenin ana amacı, nesne oluşturmayı merkezi bir noktaya taşımak ve istemcilerin hangi sınıfın nesnesine ihtiyaç duyduğunu belirlemek yerine, bir fabrika sınıfına bu görevi devretmektir. Böylece, nesne oluşturma işlemi değişikliklere karşı daha esnek ve genişletilebilir hale gelir.

Factory tasarım desenini kullanmak için genellikle bir arayüz veya soyut sınıf tarafından tanımlanan bir sınıf hiyerarşisi kullanılır. Ardından, bu hiyerarşiye ait nesneleri üretecek bir fabrika sınıfı oluşturulur.

Design Patterns


Observer




builder
builder
Observer
Bir sınıftan yeni bir nesne oluşturulduğunda kullanıcı sınıf hangi sınfı kullandığını bilir ve nesne oluşturma sürecini yönetmiş olur.
Bu bağımlılığı ortadan kaldırmak için fabrika tasarım şablonu kullanılabilir. Fabrika sınıfı kullanılan nesneyi oluşturmakla sorumludur.
Soyut sınıflar kullanıldığı taktirde, kullanıcı sınıf kendisine fabrika tarafından hangi implementasyonunun verildiğini bile bilmez.
Bu şekilde kullanıcı sınıfa değişik türde implementasyonlar verilerek, uygulamanın esnekliği artırılabilir.

Singlethon

Singleton modeli, bir sınıfın başlatılmasını kısıtlar ve sınıfın yalnızca bir örneğinin var olmasını sağlar.
Bir işlemi yerine getirmek için birden fazla yöntem (algoritma) mevcut olabilir.

Yerine göre bir yöntem seçip, uygulamak için strateji tasarım şablonu kullanılır. Her yöntem (algoritma) bir sınıf içinde implemente edilir.

Strategy tasarım deseni, bir algoritma ailesi tanımlamamızı, her birini ayrı bir sınıfa koymamızı ve nesnelerinin birbiriyle değiştirilebilir hale getirmenizi sağlayan davranışsal bir tasarım modelidir.

Example 1:
Example 1:


Adapter
Adapter

Example 1:
Example 1:


Bir nesnede meydana gelen değişikliği
içinde bulunduğu listedeki tüm elemanlara bildiren davranışsal bir tasarım modelidir.
Sistemde mevcut bulunan bir sınıfın sunduğu arayüz (metotları) başka bir sınıf tarafından kullanılabilir şekilde değiştirilir (adapte edilebilir).
Structural (yapısal) bir tasarım.
soyut fabrika tasarım şablonunda olduğu gibi istenilen bir tipte nesne oluşturmak için kullanılan oluşturucu bir tasarım modelidir.



Example Hayan 2:


Osman
Example_2:


Example Hayan 2:




Example Hayan 2:

Example Hayan 2:

Example Hayan 4:


Example_3: